/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.shephertz.appwarp.websocket.binary;

import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolEncoderAdapter;
import org.apache.mina.filter.codec.ProtocolEncoderOutput;

/**
 * Encodes incoming buffers in a manner that makes the receiving client type transparent to the
 * encoders further up in the filter chain. If the receiving client is a native client then
 * the buffer contents are simply passed through. If the receiving client is a websocket, it will encode
 * the buffer contents in to WebSocket DataFrame before passing it along the filter chain.
 *
 * Note: you must wrap the IoBuffer you want to send around a WebSocketCodecPacket instance.
 *
 * @author DHRUV CHOPRA
 */
public class WebSocketEncoder extends ProtocolEncoderAdapter{
	@Override
    public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws Exception {
        boolean isRemoteWebSocket = session.containsAttribute(WebSocketUtils.SessionAttribute) && (true==(Boolean)session.getAttribute(WebSocketUtils.SessionAttribute));
        Object result;

        if(message instanceof WebSocketHandShakeResponse){
            WebSocketHandShakeResponse response = (WebSocketHandShakeResponse)message;
            result = WebSocketEncoder.buildWSResponseBuffer(response);
        }
        else if(isRemoteWebSocket) {
        	result = WebSocketEncoder.buildWSDataFrameBuffer(message);
        }
        else {
        	result = message;
        }

        out.write(result);
    }

    // Web Socket handshake response go as a plain string.
    private static IoBuffer buildWSResponseBuffer(WebSocketHandShakeResponse response) {
        IoBuffer buffer = IoBuffer.allocate(response.getResponse().getBytes().length, false);
        buffer.setAutoExpand(true);
        buffer.put(response.getResponse().getBytes());
        buffer.flip();
        return buffer;
    }

    // Encode the in buffer according to the Section 5.2. RFC 6455
    private static IoBuffer buildWSDataFrameBuffer(Object message) throws Exception {
    	byte[] data;
    	byte type;

    	if(message instanceof String) {
    		data = ((String) message).getBytes("UTF-8");
        	type = (byte) 0x81;	// 0x81 表示文本帧
    	}
    	else if(message instanceof byte[]) {
    		data = ((byte[]) message);
        	type = (byte) 0x82;	// 0x82 表示二进制帧
    	}
    	else {
    		throw new Exception("WebSocket 无法识别要发送的数据类型：" + message.getClass().getName());
    	}

    	// 头部字节：2
    	// 长度字节长度:
    	// 0: (payload_len <= 125)
    	// 2: (payload_len == 126)
    	// 8: (payload_len == 127)
        IoBuffer buffer = IoBuffer.allocate(data.length + (2 + 8), false);

        // 填写数据类型
        buffer.put(type);

        // 填写 payload_len
        if(data.length <= 125) {
            buffer.put((byte)(data.length));
        }
        else if(data.length <= 65535) {
            buffer.put((byte)126);
            buffer.putShort((short)(data.length));
        }
        else {
            buffer.put((byte)127);
            buffer.putLong(data.length);
        }

        // 填写 payload
        buffer.put(data);

        buffer.flip();
        return buffer;
    }
}
